import json
import os.path
import pygame
from .. import setup, tools, constants


class Player(pygame.sprite.Sprite):
    def __init__(self, name):
        super().__init__()
        self.name = name
        self.load_data()
        self.setup_states()
        self.setup_velocities()
        self.setup_timers()
        self.load_image()

    def load_data(self):
        filename = self.name + ".json"
        file_path = os.path.join("sources/data/player", filename)
        with open(file_path) as f:
            self.player_data = json.load(f)

    def setup_states(self):
        self.state = "stand"
        self.face_right = True
        self.jumpable = True
        self.dead = False

    def setup_velocities(self):
        self.x_vel = 0
        self.y_vel = 0

        speed = self.player_data["speed"]
        self.max_walk_vel = speed["max_walk_speed"]
        self.max_run_vel = speed["max_walk_speed"]
        self.max_y_vel = speed["max_y_velocity"]
        self.jump_vel = speed["jump_velocity"]
        self.walk_accel = speed["walk_accel"]
        self.run_accel = speed["run_accel"]
        self.turn_accel = speed["turn_accel"]
        self.gravity = constants.GRAVITY
        self.anti_gravity = constants.ANTI_GRAVITY

        self.max_x_vel = self.max_walk_vel
        self.x_accel = self.walk_accel

    def setup_timers(self):
        self.walking_timer = 0
        self.dead_timer = 0

    def load_image(self):
        sheet = setup.graphs["mario_bros"]
        frame_rects = self.player_data["image_frames"]

        self.right_small_normal_frames = []
        self.right_big_normal_frames = []
        self.right_big_fire_frames = []
        self.left_small_normal_frames = []
        self.left_big_normal_frames = []
        self.left_big_fire_frames = []

        self.small_normal_frames = [self.right_small_normal_frames, self.left_small_normal_frames]
        self.big_normal_frames = [self.right_big_normal_frames, self.left_big_normal_frames]
        self.big_fire_frames = [self.right_big_fire_frames, self.left_big_fire_frames]

        self.all_frames = [
            self.right_small_normal_frames,
            self.right_big_normal_frames,
            self.right_big_fire_frames,
            self.left_small_normal_frames,
            self.left_big_normal_frames,
            self.left_big_fire_frames,
        ]

        # 默认的左右帧库
        self.right_frames = self.right_small_normal_frames
        self.left_frames = self.left_small_normal_frames

        # 加载移动动作
        for group, group_frame_rects in frame_rects.items():
            for frame_rect in group_frame_rects:
                right_image = tools.get_image(sheet, frame_rect["x"], frame_rect["y"], frame_rect["width"], frame_rect["height"], (0, 0, 0), constants.PLAYER_MULTI)
                left_image = pygame.transform.flip(right_image, True, False)

                if group == "right_small_normal":
                    self.right_small_normal_frames.append(right_image)
                    self.left_small_normal_frames.append(left_image)
                elif group == "right_big_normal":
                    self.right_big_normal_frames.append(right_image)
                    self.left_big_normal_frames.append(left_image)
                elif group == "right_big_fire":
                    self.right_big_fire_frames.append(right_image)
                    self.left_big_fire_frames.append(left_image)

        # 设置开始时静止动作
        self.frame_index = 0
        self.frames = self.right_frames
        self.image = self.frames[self.frame_index]
        self.rect = self.image.get_rect()

    def update(self, keys):
        self.current_time = pygame.time.get_ticks()
        self.handle_states(keys)

    def handle_states(self, keys):

        # 判断是否恢复可跳跃状态
        self.set_jumpable(keys)

        if self.state == "stand":
            self.stand(keys)
        elif self.state == "walk":
            self.walk(keys)
        elif self.state == "jump":
            self.jump(keys)
        elif self.state == "fall":
            self.fall(keys)
        elif self.state == "die":
            self.die(keys)

        if self.face_right:
            self.image = self.right_frames[self.frame_index]
        else:
            self.image = self.left_frames[self.frame_index]

    def set_jumpable(self, keys):
        if not keys[pygame.K_UP]:
            self.jumpable = True

    def stand(self, keys):
        self.frame_index = 0
        self.x_vel = 0
        self.y_vel = 0
        if keys[pygame.K_RIGHT]:
            self.face_right = True
            self.state = "walk"
        elif keys[pygame.K_LEFT]:
            self.face_right = False
            self.state = "walk"
        elif keys[pygame.K_UP] and self.jumpable:
            self.state = "jump"
            self.y_vel = self.jump_vel

    def walk(self, keys):
        # 冲刺
        if keys[pygame.K_d]:
            self.max_x_vel = self.max_run_vel
            self.x_accel = self.run_accel
        else:
            self.max_x_vel = self.max_walk_vel
            self.x_accel = self.walk_accel

        # 行走时跳跃
        if keys[pygame.K_UP] and self.jumpable:
            self.state = "jump"
            self.y_vel = self.jump_vel

        # 帧切换
        if self.current_time - self.walking_timer > self.calc_frame_duration():
            if self.frame_index < 3:
                self.frame_index += 1
            else:
                self.frame_index = 1
            self.walking_timer = self.current_time

        # 左右走动、刹车以及自然减速
        if keys[pygame.K_RIGHT]:
            self.face_right = True
            if self.x_vel < 0:
                self.frame_index = 5
                self.x_accel = self.turn_accel
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, True)
        elif keys[pygame.K_LEFT]:
            self.face_right = False
            if self.x_vel > 0:
                self.frame_index = 5
                self.x_accel = self.turn_accel
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, False)
        else:
            if self.face_right:
                self.x_vel -= self.x_accel
                if self.x_vel < 0:
                    self.x_vel = 0
                    self.state = "stand"
            else:
                self.x_vel += self.x_accel
                if self.x_vel > 0:
                    self.x_vel = 0
                    self.state = "stand"

    def jump(self, keys):
        self.frame_index = 4
        self.y_vel += self.anti_gravity
        # 设置不可再跳跃，需要松开并再次按下上键才能重新跳跃
        self.jumpable = False

        if self.y_vel >= 0 or not keys[pygame.K_UP]:
            self.state = "fall"

    def fall(self, keys):
        self.y_vel = self.calc_vel(self.y_vel, self.gravity, self.max_y_vel)

        if keys[pygame.K_RIGHT]:
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, True)
        elif keys[pygame.K_LEFT]:
            self.x_vel = self.calc_vel(self.x_vel, self.x_accel, self.max_x_vel, False)

    def die(self, keys):
        self.rect.y += self.y_vel
        self.y_vel += self.anti_gravity

    def go_die(self):
        self.dead = True
        self.y_vel = self.jump_vel
        self.frame_index = 6
        self.state = "die"
        self.dead_timer = self.current_time

    def calc_vel(self, vel, accel, max_vel, is_positive=True):
        if is_positive:
            return min(vel + accel, max_vel)
        else:
            return max(vel - accel, -max_vel)

    def calc_frame_duration(self):
        duration = 160 - 120 * abs(self.x_vel) / self.max_run_vel
        return duration

